package ch.ethz.fcl.metrobuzz.render.accessbility.view.time_scale;

import java.awt.Color;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.vecmath.Vector2d;

import org.lwjgl.opengl.GL11;
import org.newdawn.slick.opengl.Texture;

import ch.ethz.fcl.metrobuzz.MBContainer;
import ch.ethz.fcl.metrobuzz.algorithm.network.shortest_path.public_transport.PathPublic;
import ch.ethz.fcl.metrobuzz.algorithm.network.shortest_path.public_transport.PathPublic.Route;
import ch.ethz.fcl.metrobuzz.applications.AccessibilityApp;
import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Edge;
import ch.ethz.fcl.metrobuzz.data.singapore.SGMRTStations;
import ch.ethz.fcl.metrobuzz.data.texture.MBTexture;
import ch.ethz.fcl.metrobuzz.data.transit.Stop;
import ch.ethz.fcl.metrobuzz.render.accessbility.AccessibilityRender;
import ch.ethz.fcl.metrobuzz.render.techniques.colors.MBColorMap;
import ch.ethz.fcl.metrobuzz.render.techniques.colors.MBColorTable;
import ch.ethz.fcl.metrobuzz.render.techniques.projection.Projection;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Rectangle;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.tools.MBGL;
import ch.ethz.fcl.metrobuzz.tools.MBTools;
import ch.ethz.fcl.metrofont.Pen;

public class TimeScaledMap {
	private MBContainer container;
	private MBData data;

	private AccessibilityRender acceRender;

	Point2D worldCenter;

	private Color oneHourColor = Color.RED;
	private Color halfHourColor = Color.BLUE;

	private float oneHourLength;

	private float maxCost = 60 * 60;

	private MBColorMap colorMap;

	public TimeScaledMap(MBContainer container, AccessibilityRender acceRender) {
		this.container = container;
		this.data = container.getData();

		this.acceRender = acceRender;
	}

	public void render() {
		Point2D screenCenter = new Point2D(AccessibilityApp.CANVAS_WIDTH / 2,
				AccessibilityApp.CANVAS_HEIGHT / 2);

		Point3D worldCenter3d = container.getViewer().getProjection()
				.getWorldCoord(screenCenter, 0);

		worldCenter = new Point2D(worldCenter3d.getX(), worldCenter3d.getY());

		renderODStop();
		renderIsochrone();
		renderStopTimeRatio();

		renderIsoMark();
		renderDistMap();
	}

	private void renderIsochrone() {
		float screenRadius = Math.min(AccessibilityApp.CANVAS_WIDTH / 2,
				AccessibilityApp.CANVAS_HEIGHT / 2) - 10;
		oneHourLength = container.getViewer().getProjection()
				.getWorldDistance(screenRadius);

		GL11.glColor4f(oneHourColor.getRed() / 255.0f,
				oneHourColor.getGreen() / 255.0f,
				oneHourColor.getBlue() / 255.0f, 1.0f);
		GL11.glLineWidth(2.0f);

		MBGL.renderEclipse(worldCenter.getX(), worldCenter.getY(), 0,
				oneHourLength, oneHourLength, false);

		GL11.glColor4f(halfHourColor.getRed() / 255.0f,
				halfHourColor.getGreen() / 255.0f,
				halfHourColor.getBlue() / 255.0f, 1.0f);
		GL11.glLineWidth(2.0f);

		MBGL.renderEclipse(worldCenter.getX(), worldCenter.getY(), 0,
				oneHourLength / 2, oneHourLength / 2, false);
	}

	private void renderStopTimeRatio() {
		Stop origin = acceRender.getMouseAction().getOrigin();

		if (origin == null)
			return;

		Map<Stop, PathPublic> pathMap = acceRender.getFlowMapView()
				.getPathMap();

		Map<Stop, Point2D> stopPointMap = new HashMap<Stop, Point2D>();
		stopPointMap.put(origin, worldCenter);

		float aveDist = 0, maxDist = 0;
		int count = 0;

		for (Stop s : pathMap.keySet()) {
			PathPublic path = pathMap.get(s);
			if (path == null)
				continue;

			if (s.getId().equals(origin.getId()))
				continue;

			float cost = path.getCost();

			// travel cost larger than one hour
			if (cost > maxCost)
				continue;

			Vector2d dir = origin.toPoint().getDir(s.toPoint());
			Point2D p = worldCenter.getPointAt(dir, cost / maxCost
					* oneHourLength);

			stopPointMap.put(s, p);

			aveDist = (aveDist * count + path.getDist()) / (count + 1);
			maxDist = Math.max(maxDist, path.getDist());
			count++;
		}

		colorMap = new MBColorMap("res/textures/color3_power_of_2.png",
				"Distance deviation", new String[] { "0", "1",
						MBTools.formatNum("#.#", maxDist / aveDist) + "" },
				new float[] { 0, 1, maxDist / aveDist });

		GL11.glColor4f(0, 0, 0, 0.01f);
		GL11.glLineWidth(3);
		GL11.glBegin(GL11.GL_LINES);
		for (Stop s : pathMap.keySet()) {
			PathPublic path = pathMap.get(s);
			if (path == null)
				continue;

			float cost = path.getCost();

			// travel cost larger than one hour
			if (cost > maxCost)
				continue;

			for (Route route : path.getPath()) {
				// walk
				if (route.getTransitRoute() == null) {
					stopPointMap.get(route.getBegin()).render();
					stopPointMap.get(route.getEnd()).render();
				} else {
					List<Stop> stops = route.getTransitRoute().getInterStops(
							route.getBegin(), route.getEnd());

					if (stops == null)
						continue;

					for (int i = 0; i < stops.size() - 1; i++) {
						stopPointMap.get(stops.get(i)).render();

						stopPointMap.get(stops.get(i + 1)).render();
					}

				}
			}
		}
		GL11.glEnd();

		GL11.glPointSize(8);
		GL11.glBegin(GL11.GL_POINTS);
		for (Stop s : pathMap.keySet()) {
			PathPublic path = pathMap.get(s);
			if (path == null)
				continue;

			float cost = path.getCost();

			// travel cost larger than one hour
			if (cost > maxCost)
				continue;

			Color color = colorMap.getColor(path.getDist() / aveDist, 0, 0);

			GL11.glColor4f(color.getRed() / 255.0f, color.getGreen() / 255.0f,
					color.getBlue() / 255.0f, 1.0f);

			stopPointMap.get(s).render();
		}
		GL11.glEnd();
	}

	private void renderIsoMark() {
		Projection proj = container.getViewer().getProjection();

		int width = 30;
		int screenWidth = AccessibilityApp.CANVAS_WIDTH;

		int height = 100;

		Point3D p = proj.getWorldCoord(new Point2D(screenWidth - 318, height),
				0);
		float length = proj.getWorldDistance(width);

		renderShadowBackground(container, p.getX() - length / 18, p.getY()
				- length / 7, p.getX() + length * 9.4f, p.getY() + length
				* 2.2f);

		renderIsoMark(p.getX() + length / 5, p.getY(), length, halfHourColor,
				"30 min");
		renderIsoMark(p.getX() + length / 5 + length * 4, p.getY(), length,
				oneHourColor, "60 min");

		float worldFontSize = container.getViewer().getProjection()
				.getWorldDistance(20);

		container.getFont().display(
				"Isochrone counter lines",
				new Pen(p.getX() + length / 5, p.getY() + length * 1.3f,
						worldFontSize));
	}

	protected void renderShadowBackground(MBContainer container, float minX,
			float minY, float maxX, float maxY) {
		Rectangle shadowRegion = new Rectangle(minX, minY, maxX, maxY);
		MBTexture.renderTexture(container.getData().getTexture().getShadow(),
				shadowRegion);
	}

	private void renderIsoMark(float x, float y, float length, Color color,
			String label) {
		float minX = x;
		float minY = y;
		float maxX = minX + length;
		float maxY = minY + length;

		float r = color.getRed() / 255.0f, g = color.getGreen() / 255.0f, b = color
				.getBlue() / 255.0f;
		GL11.glColor3f(r, g, b);

		GL11.glBegin(GL11.GL_POLYGON);
		GL11.glVertex2f(minX, minY);
		GL11.glVertex2f(minX, maxY);
		GL11.glVertex2f(maxX, maxY);
		GL11.glVertex2f(maxX, minY);
		GL11.glEnd();

		GL11.glColor3f(0, 0, 0);
		GL11.glLineWidth(1.0f);
		GL11.glBegin(GL11.GL_LINE_STRIP);
		GL11.glVertex2f(minX, minY);
		GL11.glVertex2f(minX, maxY);
		GL11.glVertex2f(maxX, maxY);
		GL11.glVertex2f(maxX, minY);
		GL11.glVertex2f(minX, minY);
		GL11.glEnd();

		float worldFontSize = container.getViewer().getProjection()
				.getWorldDistance(20);

		container.getFont().display(
				label,
				new Pen(maxX + worldFontSize / 4, minY + length / 4,
						worldFontSize));
	}

	// render the origin at the screen center
	private void renderODStop() {

		float size = container.getViewer().getProjection().getWorldDistance(15);
		int color = MBColorTable.RED;

		GL11.glColor4f(1, 1, 1, 1);
		MBGL.renderEclipse(worldCenter.getX(), worldCenter.getY(), 0,
				size * 0.7f, size * 0.7f, true);

		MBColorTable.assignColor(color, 1.0f);
		MBGL.renderEclipse(worldCenter.getX(), worldCenter.getY(), 0,
				size * 0.3f, size * 0.3f, true);

		renderODSymbol(worldCenter.getX(), worldCenter.getY(), 30);
	}

	private void renderODSymbol(float x, float y, int screenSize) {
		float size = container.getViewer().getProjection()
				.getWorldDistance(screenSize);

		// od_symbol.png width and height
		// int width = 256;
		int height = 512;
		// coordinates of the symbol in the image
		// int minX = 45, minY = 120, maxX = 210;
		int maxY = 400;

		float imgMinX = x - size / 2;
		float imgMaxX = x + size / 2;
		float imgMinY = y - size * 2 * (height - maxY) / height;
		float imgMaxY = y + size * 2 * (maxY) / height;

		Rectangle rec = new Rectangle(imgMinX, imgMinY, imgMaxX, imgMaxY);

		Texture symbol = data.getTexture().getOriginSymbol();

		MBTexture.renderTexture(symbol, rec);
	}

	private void renderDistMap() {
		Projection proj = container.getViewer().getProjection();

		int width = 250, height = 20;
		int screenWidth = AccessibilityApp.CANVAS_WIDTH;

		int rightAlignment = 60;
		Point3D min = proj.getWorldCoord(new Point2D(screenWidth - width
				- rightAlignment, 20), 0);

		Point3D max = proj.getWorldCoord(new Point2D(screenWidth
				- rightAlignment, 20 + height), 0);

		colorMap.render(container, min.getX(), min.getY(), max.getX(),
				max.getY(), 90);
	}
}
